package com.googlecode.tracey.ui.core;

import com.googlecode.totallylazy.Callable1;
import com.googlecode.totallylazy.Callable2;
import com.googlecode.totallylazy.Pair;
import com.googlecode.utterlyidle.html.Form;

import java.util.ArrayList;
import java.util.List;

import static com.googlecode.totallylazy.Sequences.sequence;
import static com.googlecode.utterlyidle.html.Html.html;

public class FormBuilder {
    private final Form original;
    private final List<Callable1<String,String>> edits;

    public static FormBuilder modify(Form originalForm) {
        return new FormBuilder(originalForm);
    }

    private FormBuilder(Form original) {
        this.original = original;
        this.edits = new ArrayList<Callable1<String, String>>();
    }

    public Form build() throws Exception {
        return html(sequence(edits).fold(original.toString(), performEdit())).form("//form");
    }

    private Callable2<String, Callable1<String, String>, String> performEdit() {
        return new Callable2<String, Callable1<String, String>, String>() {
            @Override
            public String call(String s, Callable1<String, String> edit) throws Exception {
                return edit.call(s);
            }
        };
    }

    public FormBuilder action(String actionUri) {
        return withEdit(new SetAction(actionUri));
    }

    public FormBuilder hiddenParameter(Pair<String, String> parameter) {
        return withEdit(new AddHiddenParameter(parameter));
    }

    public FormBuilder method(String method) {
        return withEdit(new SetMethod(method));
    }

    private FormBuilder withEdit(Callable1<String,String> edit) {
        edits.add(edit);
        return this;
    }

    private static class SetMethod implements Callable1<String, String> {
        private final String method;

        public SetMethod(String method) {
            this.method = method;
        }

        @Override
        public String call(String s) throws Exception {
            return s.contains("method=") ? changeMethod(s) : insertMethod(s);
        }

        private String insertMethod(String formWithoutMethod) {
            return formWithoutMethod.replaceAll("<form ", String.format("<form method=\"%s\" ", method));
        }

        private String changeMethod(String formWithMethod) {
            return formWithMethod.replaceAll("method=\".*?\"", String.format("method=\"%s\"", method));
        }
    }
    private static class SetAction implements Callable1<String, String> {
        private final String actionUri;

        public SetAction(String actionUri) {
            this.actionUri = actionUri;
        }

        @Override
        public String call(String s) throws Exception {
            return s.replaceAll("action=\".*?\"", String.format("action=\"%s\"", actionUri));
        }
    }

    private static class AddHiddenParameter implements Callable1<String, String> {
        private final Pair<String, String> parameter;

        public AddHiddenParameter(Pair<String, String> parameter) {
            this.parameter = parameter;
        }

        @Override
        public String call(String s) throws Exception {
            return s.replaceAll("</form>", String.format("<input type=\"hidden\" name=\"%s\" value=\"%s\"/></form>", parameter.first(), parameter.second()));
        }
    }
}
